package com.wiscamp.ninechapters.tags.infrastructure.repositories;

import com.wiscamp.ninechapters.common.core.CollectionHelper;
import com.wiscamp.ninechapters.tags.domain.models.Entity;
import com.wiscamp.ninechapters.tags.domain.models.KnowledgePoint;
import com.wiscamp.ninechapters.tags.domain.models.Tag;
import com.wiscamp.ninechapters.tags.domain.repositories.EntityRepository;
import com.wiscamp.ninechapters.tags.infrastructure.converters.EntityConverter;
import com.wiscamp.ninechapters.tags.infrastructure.converters.KnowledgePointConverter;
import com.wiscamp.ninechapters.tags.infrastructure.converters.TagConverter;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.*;
import com.wiscamp.ninechapters.tags.infrastructure.po.EntityPO;
import com.wiscamp.ninechapters.tags.infrastructure.po.EntityPointPO;
import com.wiscamp.ninechapters.tags.infrastructure.po.EntityTagPO;
import cube.common.core.CollectionUtils;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

/**
 * 应用实体仓储对象
 */
@Repository
public class EntityRepositoryImpl extends BaseRepository implements EntityRepository {

    private final EntityMapper entityMapper;

    private final EntityViewMapper entityViewMapper;

    private final EntityTagMapper entityTagMapper;

    private final EntityPointMapper entityPointMapper;


    public EntityRepositoryImpl(EntityMapper entityMapper, EntityViewMapper entityViewMapper, EntityTagMapper entityTagMapper, EntityPointMapper entityPointMapper) {
        this.entityMapper = entityMapper;
        this.entityViewMapper = entityViewMapper;
        this.entityTagMapper = entityTagMapper;
        this.entityPointMapper = entityPointMapper;
    }


    // region application entity
    private EntityPO getEntityPO(long id) {
        return PersistenceUtils.findById(id, entityMapper::findById);
    }

    @HandleDataException
    public Entity getEntity(@Min(0) long entityId) {
        return PersistenceUtils.findEntityById(entityId, entityViewMapper::findById, EntityConverter.Instance::toEntityByView);
    }

    @HandleDataException
    public Entity getEntityByCode(@NotBlank String entityCode, @Min(0) long appId) {
        return PersistenceUtils.getEntityByTextAndId(entityCode, appId, entityViewMapper::findByEntityCode,
                EntityConverter.Instance::toEntityByView);
    }

    @HandleDataException
    public Collection<Entity> getEntitiesByName(@NotBlank String entityName, @Min(0) long appId) {
        return PersistenceUtils.getEntitiesByTextAndId(entityName, appId, entityViewMapper::findByEntityName,
                EntityConverter.Instance::toEntityByView);
    }

    @HandleDataException
    public Collection<Entity> getEntitiesByApplication(@Min(0) long appId) {
        return PersistenceUtils.getEntitiesById(appId, entityViewMapper::findByApplication,
                EntityConverter.Instance::toEntityByView);
    }

    @HandleDataException
    public Entity saveEntity(@NotNull Entity entity) {
        var newId = entity.getEntityId();
        if (entity.getEntityId() <= 0) {
            var newEntity = PersistenceUtils.createEntity(entity, entityMapper::saveAndFlush,
                    EntityConverter.Instance::toEntityPO, EntityConverter.Instance::toEntity);
            newId = newEntity.getEntityId();
        } else {
            var existedPO = getEntityPO(entity.getEntityId());
            if (existedPO == null) existedPO = new EntityPO();
            PersistenceUtils.updateEntity(entity, existedPO, entityMapper::saveAndFlush,
                    EntityConverter.Instance::toEntityPO, EntityConverter.Instance::toEntity);
        }
        return getEntity(newId);
    }

    // endregion

    //region entityTag
    private EntityTagPO getEntityTag(long entityId, long primaryKeyId, long tagId) {
        return entityTagMapper.findEntityTag(entityId, primaryKeyId, tagId);
    }

    @HandleDataException
    public Collection<Tag> getTagsByPrimaryKey(long entityId, long primaryKeyId) {
        var tagViews = entityTagMapper.findTagsByPrimaryKey(entityId, primaryKeyId);
        return CollectionUtils.toArrayList(tagViews, TagConverter.Instance::toTagByView);
    }

    @HandleDataException
    public Collection<Long> getPrimaryIdsByTags(@Min(0) long entityId,
                                                @NotEmpty Collection<Long> tagIds) {
        return entityTagMapper.findPrimaryIdsByTags(entityId, tagIds);
    }

    public boolean bindTag(long appId, long entityId, long primaryKeyId, long tagId) {
        var entityTagPO = getEntityTag(entityId, primaryKeyId, tagId);
        if (Objects.isNull(entityTagPO)) {
            entityTagPO = new EntityTagPO();
            entityTagPO.setApplicationId(appId);
            entityTagPO.setEntityId(entityId);
            entityTagPO.setPrimaryKeyId(primaryKeyId);
            entityTagPO.setTagId(tagId);
            entityTagMapper.saveAndFlush(entityTagPO);
        }
        return true;
    }

    public boolean unbindTag(long appId, long entityId, long primaryKeyId, long tagId) {
        var entityTagPO = getEntityTag(entityId, primaryKeyId, tagId);
        if (Objects.nonNull(entityTagPO)) {
            entityTagMapper.delete(entityTagPO);
        }
        return true;
    }
    //endregion


    //region entityPoint
    private EntityPointPO getEntityPoint(long entityId, long entityKeyId, long pointId) {
        return entityPointMapper.findEntityPoint(entityId, entityKeyId, pointId);
    }

    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsByPrimaryKey(long entityId, long primaryKeyId) {
        var pointViews = entityPointMapper.findPointsByPrimaryId(entityId, primaryKeyId);
        return CollectionUtils.toArrayList(pointViews, KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsByPrimaryKey(long entityId, long primaryKeyId, boolean isPrimary) {
        var pointViews = entityPointMapper.findPointsByPrimaryId(entityId, primaryKeyId, isPrimary);
        return CollectionUtils.toArrayList(pointViews, KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    /**
     * 获取一组应用实体主键关联的知识点列表
     *
     * @param appId
     * @param entityId
     * @param primaryKeyIds
     * @return
     */
    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsByPrimaryKeys(@Min(0) long entityId,
                                                                      @NotBlank Collection<Long> primaryKeyIds) {
        var pointViews = entityPointMapper.findPointsByPrimaryIds(entityId, primaryKeyIds);
        return CollectionHelper.convertToArrayList(pointViews, KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public Collection<Long> getPrimaryKeyIdsByKnowledgePoints(@Min(0) long entityId,
                                                              @NotBlank Collection<Long> pointIds) {
        return entityPointMapper.findPrimaryIdsByPoints(entityId, pointIds);
    }

    public boolean bindKnowledgePoint(long appId, long entityId, long primaryKeyId, long pointId, boolean isPrimary) {
        var entityPointPO = getEntityPoint(entityId, primaryKeyId, pointId);
        if (Objects.isNull(entityPointPO)) {
            entityPointPO = new EntityPointPO();
            entityPointPO.setApplicationId(appId);
            entityPointPO.setEntityId(entityId);
            entityPointPO.setPrimaryKeyId(primaryKeyId);
            entityPointPO.setPointId(pointId);
            entityPointPO.setPrimary(isPrimary);
            entityPointMapper.saveAndFlush(entityPointPO);
        }
        return true;
    }

    public boolean unbindKnowledgePoint(long appId, long entityId, long primaryKeyId, long pointId) {
        var entityPointPO = getEntityPoint(entityId, primaryKeyId, pointId);
        if (Objects.nonNull(entityPointPO)) {
            entityPointMapper.delete(entityPointPO);
        }
        return true;
    }

    /**
     * 获取一个知识点关联的应用实体主键数量
     *
     * @param appId
     * @param entityId
     * @param pointId
     * @return
     */
    @HandleDataException
    public long countPrimaryKeyIdsByPoint(@Min(0) long entityId, @Min(0) long pointId) {
        return entityPointMapper.countPrimaryIdsByPoint(entityId, pointId);
    }
    //endregion
}
